let date = Date.now();
date.toLocaleString
import express from 'express'
import crypto from "crypto";
import bodyParser from "body-parser";
import xml2js from 'xml2js'
import commander from "commander";

type PredefinedResponse = {
    keywords: string | Array<string | string[]>,
    msgs: string | string[]
}
type UserPredefinedResponse = {
    ids: string[],
    name: string,
    default: string | string[],
    responses: PredefinedResponse[]
}
type PredefinedResponses = {
    all: PredefinedResponse[],
    users: UserPredefinedResponse[]
}

let cmd = commander.createCommand();
cmd.storeOptionsAsProperties(false);
cmd.option('--token [token]');
cmd.option('--predefine [file]');
cmd.parse(process.argv);
let opts  = cmd.opts();
let predefinedMsgs: PredefinedResponses = require(opts.predefine);
let token = opts.token;
let IdToName: Record<string, string> = {};
predefinedMsgs.users.forEach(e => {
    e.ids.forEach(id => {
        IdToName[id] = e.name;
    });
    e.responses.push(...predefinedMsgs.all);
});
let app = express();
app.get("/", (req, res, next) => {
    if(req.query.echostr) {
        res.status(200).send(req.query.echostr);
    } else {
        next();
    }
});
// https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
type MsgType = 'text' | 'voice' | 'video' | 'shortvideo' | 'location' | 'link';
type MessageEnvelop = {
    ToUserName: string,
    FromUserName: string,
    CreateTime: number,
    MsgType: MsgType,
    Content: string,
    MsgId: number
}
let xmlParser: xml2js.Parser = new xml2js.Parser({
    explicitArray: false
});
const WEEKS:string = "日一二三四五六日";
let getResponse:(userId: string, msg:string) => string = (function(){

    function getRandomInt(max: number): number {
        return Math.floor(Math.random() * Math.floor(max));
    }

    function isMatch(msg: string, predefinedRes: PredefinedResponse): boolean {
        if(!predefinedRes.keywords) {
            return true;
        }
        if(typeof predefinedRes.keywords === 'string') {
            return msg.indexOf(predefinedRes.keywords as string) >= 0;
        }
        nextKeyword:
        for(let i = 0; i < predefinedRes.keywords.length; i++) {
            let keyword = predefinedRes.keywords[i];
            if(typeof keyword === 'string') {
                if(msg.indexOf(keyword as string) >= 0) {
                    return true;
                }

            } else {
                let array:string[] = keyword as string[];
                for(let j = 0; j < array.length; j++) {
                    if(!msg.indexOf(array[j])) {
                        continue nextKeyword;
                    }
                }
                return true;
            }
        }
        return false;
    }
    function getOne(msgs: string | string[]): string {
        if(typeof msgs === "string") {
            return msgs as string;
        } else {
            return msgs[getRandomInt(msgs.length)];
        }

    }
    function find(findIn:PredefinedResponse[] | undefined, msg: string):string|undefined {
        if(findIn == undefined) {
            return undefined;
        }
        let found = findIn.find(e => isMatch(msg, e));
        if(!found) {
            return undefined;
        }
        return getOne(found.msgs);
    }
    return function(userId: string, msg:string): string {
        let userDefined:UserPredefinedResponse | undefined = predefinedMsgs.users.find( e => e.ids.find(e2 => e2 === userId));
        if(!userDefined) {
            return 'ok'
        }
        let found = find(userDefined.responses, msg);
        return found || getOne(userDefined.default);
    }
})();

app.post("/", bodyParser.text({type:"*/*"}), (req, res) => {
    xmlParser.parseString(req.body, (_error:any, result:{xml: MessageEnvelop}) =>{

        let msgEnvelop = result?.xml;
        if(msgEnvelop.FromUserName in IdToName) {
            let query = req.query;
            let signature:string = query.signature as string;
            let timestamp:string = query.timestamp as string;
            let nonce:string = query.nonce as string;
            let tmpArray:string[] = [token, timestamp, nonce];
            tmpArray.sort();
            let sha1 = crypto.createHash('sha1').update(tmpArray.join('')).digest('hex');
            if(sha1 === signature) {
                let name = IdToName[msgEnvelop.FromUserName];
                let date:Date = new Date(msgEnvelop.CreateTime * 1000);
                let dateStr = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}(${WEEKS.charAt(date.getDay())}) ${date.getHours()}:${date.getMinutes() + 1}`;
                console.log(`${name}[${dateStr}]:${msgEnvelop.Content}`);
                let resMsg = getResponse(msgEnvelop.FromUserName, msgEnvelop.Content);
                res.status(200).send(`<xml><ToUserName><![CDATA[${msgEnvelop.FromUserName}]]></ToUserName><FromUserName><![CDATA[${msgEnvelop.ToUserName}]]></FromUserName><CreateTime>${Date.now()}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[${resMsg}]]></Content></xml>`)
                return;
            }

        } else {
            res.status(200).send(`<xml><ToUserName><![CDATA[${msgEnvelop.FromUserName}]]></ToUserName><FromUserName><![CDATA[${msgEnvelop.ToUserName}]]></FromUserName><CreateTime>${Date.now()}</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[你谁？]]></Content></xml>`)
            return;
        }
        res.status(200).send("success");
    });
})

app.listen(8080)